package org.convallaria.system.biz.service.auth.impl;

import cn.hutool.core.text.CharSequenceUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.infrastruct.ce.util.CacheKeyGenerator;
import org.convallaria.infrastruct.ce.util.CacheUtils;
import org.convallaria.infrastruct.ce.util.RedisCacheUtils;
import org.convallaria.system.biz.entity.dto.CaptchaDTO;
import org.convallaria.system.biz.service.auth.CaptchaService;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.util.Base64;
import java.util.Random;
import java.util.Set;
import java.util.UUID;

/**
 * 验证码服务实现
 *
 * @author convallaria
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CaptchaServiceImpl implements CaptchaService {

    // 使用ce模块的缓存工具
    private final RedisCacheUtils redisCacheUtils;
    private final CacheUtils cacheUtils;
    private final CacheKeyGenerator keyGenerator;

    // 缓存配置常量
    private static final String CAPTCHA_CACHE_NAME = "captcha";
    private static final String CAPTCHA_PREFIX = "captcha:";
    private static final int CAPTCHA_EXPIRE_MINUTES = 5;
    private static final int CAPTCHA_LENGTH = 4;
    private static final int IMAGE_WIDTH = 120;
    private static final int IMAGE_HEIGHT = 40;

    private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    private static final Random RANDOM = new Random();

    @Override
    public CaptchaDTO generateCaptcha() {
        try {
            String captchaKey = UUID.randomUUID().toString();
            String captchaCode = generateRandomCode(CAPTCHA_LENGTH);
            
            // 生成缓存键
            String cacheKey = keyGenerator.generateKey(CAPTCHA_PREFIX, captchaKey);

            // 使用RedisCacheUtils存储验证码（主要存储）
            redisCacheUtils.set(cacheKey, captchaCode, Duration.ofMinutes(CAPTCHA_EXPIRE_MINUTES));
            
            // 使用CacheUtils存储到通用缓存（仅用于统计和监控，不设置过期时间）
            cacheUtils.put(CAPTCHA_CACHE_NAME, cacheKey, captchaCode);

            // 生成图片验证码
            BufferedImage captchaImage = generateCaptchaImage(captchaCode);
            String base64Image = imageToBase64(captchaImage);

            log.info("生成验证码成功: key={}, code={}", captchaKey, captchaCode);

            return CaptchaDTO.builder()
                    .captchaKey(captchaKey)
                    .captchaImage(base64Image)
                    .expireTime(System.currentTimeMillis() + CAPTCHA_EXPIRE_MINUTES * 60 * 1000L)
                    .build();
        } catch (Exception e) {
            log.error("生成验证码失败", e);
            throw new RuntimeException("生成验证码失败", e);
        }
    }

    @Override
    public boolean validateCaptcha(String captchaKey, String captchaCode) {
        if (CharSequenceUtil.isBlank(captchaKey) || CharSequenceUtil.isBlank(captchaCode)) {
            log.warn("验证码参数为空: captchaKey={}, captchaCode={}", captchaKey, captchaCode);
            return false;
        }

        try {
            // 生成缓存键
            String cacheKey = keyGenerator.generateKey(CAPTCHA_PREFIX, captchaKey);
            
            // 使用RedisCacheUtils获取验证码
            String storedCode = redisCacheUtils.get(cacheKey, String.class);
            if (storedCode == null) {
                log.warn("验证码已过期或不存在: {}", captchaKey);
                return false;
            }

            // 验证验证码
            boolean isValid = captchaCode.equalsIgnoreCase(storedCode);

            if (isValid) {
                // 验证成功后删除验证码（一次性使用）
                redisCacheUtils.delete(cacheKey);
                cacheUtils.evict(CAPTCHA_CACHE_NAME, cacheKey);
                log.info("验证码验证成功: {}", captchaKey);
            } else {
                log.warn("验证码验证失败: {} - 输入: {}, 正确: {}", captchaKey, captchaCode, storedCode);
            }

            return isValid;
        } catch (Exception e) {
            log.error("验证码校验异常: captchaKey={}", captchaKey, e);
            return false;
        }
    }

    /**
     * 生成随机验证码
     */
    private String generateRandomCode(int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            sb.append(CHARS.charAt(RANDOM.nextInt(CHARS.length())));
        }
        return sb.toString();
    }

    /**
     * 生成验证码图片
     */
    private BufferedImage generateCaptchaImage(String code) {
        BufferedImage image = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = image.createGraphics();

        // 设置背景色
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);

        // 设置字体
        Font font = new Font("Arial", Font.BOLD, 24);
        g2d.setFont(font);

        // 绘制验证码
        for (int i = 0; i < code.length(); i++) {
            // 随机颜色
            g2d.setColor(new Color(RANDOM.nextInt(256), RANDOM.nextInt(256), RANDOM.nextInt(256)));

            // 随机位置
            int x = 20 + i * 20 + RANDOM.nextInt(10);
            int y = 25 + RANDOM.nextInt(10);

            // 绘制字符
            g2d.drawString(String.valueOf(code.charAt(i)), x, y);
        }

        // 添加干扰线
        for (int i = 0; i < 5; i++) {
            g2d.setColor(new Color(RANDOM.nextInt(256), RANDOM.nextInt(256), RANDOM.nextInt(256)));
            g2d.drawLine(RANDOM.nextInt(IMAGE_WIDTH), RANDOM.nextInt(IMAGE_HEIGHT),
                    RANDOM.nextInt(IMAGE_WIDTH), RANDOM.nextInt(IMAGE_HEIGHT));
        }

        g2d.dispose();
        return image;
    }

    /**
     * 图片转Base64
     */
    private String imageToBase64(BufferedImage image) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", baos);
        byte[] imageBytes = baos.toByteArray();
        return "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
    }

    // ==================== 新增功能：利用ce模块缓存特性 ====================

    /**
     * 检查验证码是否存在（不删除）
     * 
     * @param captchaKey 验证码key
     * @return 是否存在
     */
    @Override
    public boolean existsCaptcha(String captchaKey) {
        if (CharSequenceUtil.isBlank(captchaKey)) {
            return false;
        }
        
        String cacheKey = keyGenerator.generateKey(CAPTCHA_PREFIX, captchaKey);
        return redisCacheUtils.exists(cacheKey);
    }

    /**
     * 获取验证码剩余过期时间
     * 
     * @param captchaKey 验证码key
     * @return 剩余时间（秒）
     */
    @Override
    public long getCaptchaExpireTime(String captchaKey) {
        if (CharSequenceUtil.isBlank(captchaKey)) {
            return -1;
        }
        
        String cacheKey = keyGenerator.generateKey(CAPTCHA_PREFIX, captchaKey);
        return redisCacheUtils.getExpire(cacheKey);
    }

    /**
     * 刷新验证码过期时间
     * 
     * @param captchaKey 验证码key
     * @return 是否刷新成功
     */
    @Override
    public boolean refreshCaptchaExpire(String captchaKey) {
        if (CharSequenceUtil.isBlank(captchaKey)) {
            return false;
        }
        
        String cacheKey = keyGenerator.generateKey(CAPTCHA_PREFIX, captchaKey);
        
        // 先检查验证码是否存在
        if (!redisCacheUtils.exists(cacheKey)) {
            log.warn("验证码不存在，无法刷新: {}", captchaKey);
            return false;
        }
        
        // 刷新Redis中的过期时间
        boolean refreshed = redisCacheUtils.expire(cacheKey, Duration.ofMinutes(CAPTCHA_EXPIRE_MINUTES));
        
        if (refreshed) {
            // 同步更新CacheUtils中的缓存（重新存储，确保一致性）
            String captchaCode = redisCacheUtils.get(cacheKey, String.class);
            if (captchaCode != null) {
                cacheUtils.put(CAPTCHA_CACHE_NAME, cacheKey, captchaCode);
            }
            log.info("验证码过期时间刷新成功: {}", captchaKey);
        }
        
        return refreshed;
    }

    /**
     * 清理过期的验证码
     * 
     * @return 清理的数量
     */
    @Override
    public long cleanExpiredCaptchas() {
        try {
            String pattern = CAPTCHA_PREFIX + "*";
            long cleanedCount = 0;
            
            // 获取所有验证码键
            Set<String> allKeys = redisCacheUtils.keys(pattern);
            if (allKeys.isEmpty()) {
                log.info("没有找到验证码，无需清理");
                return 0;
            }
            
            log.info("开始检查 {} 个验证码的过期状态", allKeys.size());
            
            // 逐个检查每个验证码是否过期
            for (String key : allKeys) {
                try {
                    // 检查键是否存在（Redis会自动删除过期的键）
                    if (!redisCacheUtils.exists(key)) {
                        // 键不存在说明已过期，清理CacheUtils中的对应缓存
                        cacheUtils.evict(CAPTCHA_CACHE_NAME, key);
                        cleanedCount++;
                        log.debug("清理过期验证码: {}", key);
                    } else {
                        // 键存在，检查剩余过期时间
                        long expireTime = redisCacheUtils.getExpire(key);
                        if (expireTime <= 0) {
                            // 过期时间<=0说明已过期，删除
                            redisCacheUtils.delete(key);
                            cacheUtils.evict(CAPTCHA_CACHE_NAME, key);
                            cleanedCount++;
                            log.debug("清理过期验证码: {} (剩余时间: {}秒)", key, expireTime);
                        } else {
                            log.debug("验证码未过期: {} (剩余时间: {}秒)", key, expireTime);
                        }
                    }
                } catch (Exception e) {
                    log.warn("检查验证码过期状态失败: {}", key, e);
                    // 如果检查失败，为了安全起见，删除该键
                    redisCacheUtils.delete(key);
                    cacheUtils.evict(CAPTCHA_CACHE_NAME, key);
                    cleanedCount++;
                }
            }
            
            log.info("清理过期验证码完成，共清理 {} 个，保留 {} 个有效验证码", 
                    cleanedCount, allKeys.size() - cleanedCount);
            return cleanedCount;
        } catch (Exception e) {
            log.error("清理过期验证码失败", e);
            return 0;
        }
    }
}
